package com.lk.oauth2.server.config;

import java.security.Security;

import javax.sql.DataSource;
import javax.xml.crypto.Data;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * @description: 认证服务器配置类
 * @author: Aspirin
 * @create: 2020-08-30 09:45
 * @description: @EnableAuthorizationServer 开启了认证服务器
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

  private static final Logger logger = LoggerFactory.getLogger(AuthorizationServerConfig.class);

  @Autowired private PasswordEncoder passwordEncoder;
  @Autowired private DataSource dataSource;
  @Autowired private AuthenticationManager authenticationManager;
  // 刷新令牌
  @Autowired private UserDetailsService customUserDetailsService;
  // token管理方式
  @Autowired private TokenStore tokenStore;
  @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter;

  /** 注入JdbcClientDetailsService，用于JDBC管理客户端信息 */
  @Bean
  public ClientDetailsService jdbcClientDetailsService() {
    return new JdbcClientDetailsService(dataSource);
  }

  /**
   * 被允许访问此认证服务器的客户端信息 1. 内存方式 2. 数据库方式
   *
   * @param clients
   * @throws Exception
   */
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    // "lk-pc":客户端id,"lk-secret":客户端密码,加密
    // 内存方式管理客户端信息:客户端少可以写在配置文件里
    /*        clients
    .inMemory()
    // 客户端id
    .withClient("lk-pc")
    .secret(passwordEncoder.encode("lk-secret"))
    // 资源id,针对是微服务的名称，比如商品管理
    .resourceIds("product-server")
    // 指定授权模式：授权码模式，密码模式，简化模式，客户端模式，刷新令牌
    .authorizedGrantTypes(
        "authorization_code", "password", "implicit", "client_credentials", "refresh_token")
    // 授权范围标识：哪部分资源可以访问
    .scopes("all")
    // false 跳到一个授权页面手动点击授权，true不需要手动点授权，直接响应一个授权码
    .autoApprove(false)
    // 客户端回调地址(授权码会在这个地址后面拼接)
    .redirectUris("http://www.baidu.com/")
    // token 默认有效时长是12小时
    .accessTokenValiditySeconds(60 * 60 * 8)
    // 刷新令牌有效时长,默认是30天
    .refreshTokenValiditySeconds(60 * 60 * 24 * 60);*/

    logger.info("加密后:{}", passwordEncoder.encode("lk-secret"));
    // jdbc管理客户端[推荐]
    clients.withClientDetails(jdbcClientDetailsService());
  }

  /** 授权码管理策略 */
  @Bean
  public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
    return new JdbcAuthorizationCodeServices(dataSource);
  }
  /**
   * 关于认证服务器端点设置
   *
   * @param endpoints
   * @throws Exception
   */
  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    // 密码模式要设置认证管理器
    endpoints.authenticationManager(authenticationManager);

    // 刷新令牌的时候需要试用
    endpoints.userDetailsService(customUserDetailsService);

    // 令牌的管理方式
    endpoints.tokenStore(tokenStore).accessTokenConverter(jwtAccessTokenConverter);

    // 授权码管理策略 就会把产生的授权码 oauth_code这张表中，如果这个授权码已经被使用了，则对应这个表中的数据就会被删除
    endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices());
  }

  /**
   * @param security
   * @throws Exception
   */
  @Override
  public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    // 所有人可访问 /oauth/token_key 后面要获取公钥, 默认拒绝访问
    security.tokenKeyAccess("permitAll()");
    // 认证后可访问 /oauth/check_token , 默认拒绝访问
    security.checkTokenAccess("isAuthenticated()");
  }
}
